package optimizers.cmaes.dnacomputing;

import java.util.HashMap;
import java.util.Random;

import fitness.SquareFitness;

import model.Constants;
import model.OligoSystemGeneral;
import model.SimpleSequence;

import visualization.GraphDisplayer;

import optimizers.cmaes.CMAEvolutionStrategy;
import optimizers.cmaes.fitness.IObjectiveFunction;
import optimizers.commons.RunConfig;
import optimizers.ganeat.square.IndividualSquare;
import optimizers.tools.FileManager;
import optimizers.tools.Misc;
import runconfiguration.SquareConfiguration;

/*
 * 2013-02-07: handle lower bound value (replace x[index]<0?x=0:x=x[index] by x[index]*x[index] )
 */

/**
 * DNA Computing interface for fitness evaluation
 */
class DNACompFitnessHuy implements IObjectiveFunction { // meaning implements
														// methods valueOf and
														// isFeasible

	final static boolean COMPLEX_FITNESS = false;

	public static FileManager myLogFile;

	int numberOfSimpleSequences = -1;
	public int currentGen = 0; // not used for the moment
	public int currentInd = 0; // not used for the moment

	int evaluation = 0;

	double bestEverValue = Double.NEGATIVE_INFINITY;

	public DNACompFitnessHuy(int __numberOfSimpleSequences) {
		this.numberOfSimpleSequences = __numberOfSimpleSequences;
	}

	public void setInfo(int __currentGen, int __currentInd) // NOT USED FOR THE
															// MOMENT
	{
		this.currentGen = __currentGen;
		this.currentInd = __currentInd;
	}

	// ***

	public double convertValue(double x) {
		double res;

		// res = x*x; // the advised method. HOWEVER this turns out to be
		// computationally very extensive because of non-sparse matrix.
		// res = x<0?0:x ; // violent bound at zero, not classical, but in this
		// particular case this may help as sparse matrix becomes easy to set.

		if (x > 0.5) // \_/, centered on 0.
			res = x - 0.5;
		else if (x < -0.5)
			res = -x - 0.5;
		else
			res = 0;

		return res;
	}

	// ***

	public double valueOf(double[] x) {
		RunConfig config = new SquareConfiguration();
		IndividualSquare templateIndividual = new IndividualSquare(
				new HashMap<String, Integer>(), new HashMap<String, String>(),
				-1, 0, config, "Node gens:\r\n" + " a 20.0431 20\r\n"
						+ " c 58.6952 1\r\n" + " Icc 0.587 1\r\n"
						+ " i 25.2767 1\r\n" + " n 24.1125 1\r\n"
						+ " Inn 0.2411 1\r\n" + " Iii 0.2528 1\r\n"
						+ "Connection gens:\r\n" + " 1 a->a false 3.7959\r\n"
						+ " 2 a->c false 0.2367\r\n"
						+ " 3 c->a false 4.6433\r\n"
						+ " 9 c->c true 21.0849\r\n"
						+ " 10 a->Icc true 23.968\r\n"
						+ " 33 c->i false 2.5251\r\n"
						+ " 34 i->a true 2.9932\r\n"
						+ " 43 i->i true 55.7416\r\n"
						+ " 56 c->n false 0.358\r\n"
						+ " 57 n->i false 0.3764\r\n"
						+ " 63 n->n true 14.9429\r\n"
						+ " 64 c->Inn true 43.8504\r\n"
						+ " 69 n->Iii true 42.127\r\n" + "Exo: 0\r\n"
						+ "Pol: 0\r\n" + "Nick: 0 ");
		OligoSystemGeneral oligoSystem = templateIndividual.getOligoModel();

		double[][][] template = new double[numberOfSimpleSequences + 1][numberOfSimpleSequences][numberOfSimpleSequences];
		double[][] seqK = new double[numberOfSimpleSequences + 1][numberOfSimpleSequences];
		double[][] seqC = new double[numberOfSimpleSequences + 1][numberOfSimpleSequences];
		double[][] inhK = new double[numberOfSimpleSequences + 1][numberOfSimpleSequences];

		int count = 0;

		for (int i = 0; i < numberOfSimpleSequences + 1; i++) {
			for (int j = 0; j < numberOfSimpleSequences; j++) {
				for (int k = 0; k < numberOfSimpleSequences; k++) {
					template[i][j][k] = x[count];
					count++;
				}
			}
		}

		for (int i = 0; i < numberOfSimpleSequences + 1; i++) {
			for (int j = 0; j < numberOfSimpleSequences; j++) {
				seqK[i][j] = x[count];
				count++;
			}
		}

		for (int i = 0; i < numberOfSimpleSequences + 1; i++) {
			for (int j = 0; j < numberOfSimpleSequences; j++) {
				seqC[i][j] = oligoSystem.seqConcentration[i][j];
			}
		}

		for (int i = 0; i < numberOfSimpleSequences + 1; i++) {
			for (int j = 0; j < numberOfSimpleSequences; j++) {
				inhK[i][j] = oligoSystem.inhK[i][j];
			}
		}

		double exo = 0;

		OligoSystemGeneral newOligoSystem = new OligoSystemGeneral(
				numberOfSimpleSequences, template, seqK, inhK, seqC, exo,
				templateIndividual.getOligoModel().intToName,
				templateIndividual.getOligoModel().nameToInt);
		SquareFitness fitnessEvaluator = new SquareFitness();
		return -fitnessEvaluator.evaluateFitness(newOligoSystem).finalResult();
	}

	// ***

	public boolean isFeasible(double[] x) {
		return true;
	} // entire R^n is feasible
}

public class CMA_DNAComp_Huy1 {
	public static void main(String[] args) {

		int numberOfSimpleSequences = 3; // Matrix size is nxnx(n+1) -- value in
											// [4...6]

		IObjectiveFunction fitfun = new DNACompFitnessHuy(numberOfSimpleSequences);

		// new a CMA-ES and set some initial values
		CMAEvolutionStrategy cma = new CMAEvolutionStrategy();

		// cma.readProperties(); // read options, see file
		// CMAEvolutionStrategy.properties
		cma.readProperties("src/optimizers/config/CMAES-DNA-20130207-Huy.properties");

		cma.setDimension(numberOfSimpleSequences * numberOfSimpleSequences
				* (numberOfSimpleSequences + 1) + (numberOfSimpleSequences + 1)
				* numberOfSimpleSequences); // overwrite
											// some
											// loaded
											// properties

		cma.setInitialX(0.0); // in each dimension, also setTypicalX can be used
		cma.setInitialStandardDeviation(1.5); // 0.15 // also a mandatory
												// setting
		// cma.options.stopFitness = 1e-14; // optional setting

		// initialize cma and get fitness array to fill in later
		double[] fitness = cma.init(); // new
										// double[cma.parameters.getPopulationSize()];

		String timeStamp = Misc.getCurrentTimeAsCompactString();

		// initial output to files
		// cma.writeToDefaultFilesHeaders(0); // 0 == overwrites old files
		cma.writeToDefaultFilesHeaders(
				"logs-optimizers/log_" + timeStamp + "-", 0);

		cma.helloWorld();

		// log info
		((DNACompFitnessHuy) fitfun).currentGen = 0;
		((DNACompFitnessHuy) fitfun).currentInd = 0;

		System.out.println("Time stamp: " + timeStamp);
		System.out
				.println("# evaluation,currentGen,currentInd,currentFitess,bestFitnessEver");

		((DNACompFitnessHuy) fitfun).myLogFile = new FileManager(
				"logs-optimizers/log-cmaes-" + timeStamp + ".txt");

		((DNACompFitnessHuy) fitfun).myLogFile
				.write("# evaluation,currentGen,currentInd,currentFitess,bestFitnessEver\n");

		// iteration loop
		while (cma.stopConditions.getNumber() == 0) {

			// --- core iteration step ---
			double[][] pop = cma.samplePopulation(); // get a new population of
														// solutions
			for (int i = 0; i < pop.length; ++i) { // for each candidate
													// solution i
				// a simple way to handle constraints that define a convex
				// feasible domain
				// (like box constraints, i.e. variable boundaries) via
				// "blind re-sampling"
				// assumes that the feasible domain is convex, the optimum is
				while (!fitfun.isFeasible(pop[i]))
					// not located on (or very close to) the domain boundary,
					pop[i] = cma.resampleSingle(i); // initialX is feasible and
													// initialStandardDeviations
													// are
													// sufficiently small to
													// prevent quasi-infinite
													// looping here
				// System.out.print(".");

				// compute fitness/objective value
				fitness[i] = fitfun.valueOf(pop[i]); // fitfun.valueOf() is to
														// be minimized

				((DNACompFitnessHuy) fitfun).currentInd++;
			}

			// System.out.print("\n");

			cma.updateDistribution(fitness); // pass fitness array to update
												// search distribution
			// --- end core iteration step ---

			// output to files and console
			cma.writeToDefaultFiles();
			int outmod = 150;
			if (cma.getCountIter() % (15 * outmod) == 1)
				cma.printlnAnnotation(); // might write file as well
			if (cma.getCountIter() % outmod == 1)
				cma.println();

			((DNACompFitnessHuy) fitfun).currentGen++;
			((DNACompFitnessHuy) fitfun).currentInd = 0;
		}
		// evaluate mean value as it is the best estimator for the optimum
		cma.setFitnessOfMeanX(fitfun.valueOf(cma.getMeanX())); // updates the
																// best ever
																// solution

		// final output
		cma.writeToDefaultFiles(1);
		cma.println();
		cma.println("Terminated due to");
		for (String s : cma.stopConditions.getMessages())
			cma.println("  " + s);
		cma.println("best function value " + cma.getBestFunctionValue()
				+ " at evaluation " + cma.getBestEvaluationNumber());

		// we might return cma.getBestSolution() or cma.getBestX()

		((DNACompFitnessHuy) fitfun).myLogFile.close();

	} // main
} // class

